/* 
 * Copyright 2009 Shared Object (www.sharedobject.ch)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package edos.prm.actor;

import java.util.HashSet;
import java.util.Set;

import edos.prm.activity.Activity;
import edos.prm.activity.ActivityManager.PrmActivityManager;
import edos.prm.actor.Actor.PrmActor;
import edos.prm.artifact.Artifact;
import edos.prm.artifact.ArtifactManager;
import edos.prm.artifact.ArtifactSet;
import edos.prm.artifact.AttributePair;
import edos.prm.artifact.Directory;
import edos.prm.artifact.UnregisteredArtifactType;
import edos.prm.artifact.ArtifactManager.PrmArtifactManager;
import edos.prm.engine.PRM;
import edos.prm.engine.PrmSecurityManager;
import edos.prm.project.Topic;
import edos.prm.role.Role;
import edos.prm.role.RoleException;
import edos.prm.util.Time;
import edos.prm.util.VersionNumber;

/**
 * The {@code ActorManager} activity is responsible for management of actors
 * within projects.
 * <p>
 * An {@code Actor} is characterized by {@code interests} and
 * {@code experience}; these qualities are required to help actors locate other
 * actors in an effort to build and consolidate a project.
 * 
 * @version 1.0
 */
public interface ActorManager extends Activity {

	/**
	 * Adds a new Actor to the community
	 *
	 * @param cinfo the contact information for this actor
	 * @return a new Actor with the specified contact information
	 * @throws RoleException if executing task does not have permission to
	 * execute this method.
	 */
	public Actor newActor(ContactInformation cinfo) throws RoleException;

	/**
	 * Retrieves all Actors that have been registered with the PRM.
	 * 
	 * @return all Actors registered with the PRM.
	 */
	public Set<Actor> getActors();

	/**
	 * Searches for Actors corresponding to given attributes.
	 * 
	 * @param attributes characteristics returned Actors have to match
	 * @return Actors corresponding to given characteristics
	 * @throws RoleException if executing task does not have permission to
	 * execute this method.
	 */
	public Set<Actor> find(AttributePair<?>...attributes) throws RoleException;

	/**
	 * Searches for Actors corresponding to given interests and competences
	 * 
	 * @param interests interests the actors should match
	 * @param experience the actors should match
	 * @return Actors corresponding to given characteristics
	 * @throws RoleException if executing task does not have permission to
	 * execute this method.
	 */
	public Set<Actor> find(Topic[] interests, Topic[] experience)
	throws RoleException;

	/**
	 * Retrieves the experience of a specific actor
	 * 
	 * @param actor the Actor whose experience is being queried
	 * @return the topics representing the experience of the actor
	 * @throws RoleException if executing task does not have permission to
	 * execute this method.
	 */
	public Set<Topic> getExperience(Actor actor) throws RoleException;

	/**
	 * Retrieves the interests of a specific actor.
	 * 
	 * @param actor the Actor whose interests are being queried
	 * @return the topics representing the interests of the actor
	 * @throws RoleException if executing task does not have permission to
	 * execute this method.
	 */
	public Set<Topic> getInterests(Actor actor) throws RoleException;

	/**
	 * Retrieves the roles assigned to an actor
	 * 
	 * @param actor the Actor whose roles are being queried
	 * @return the roles assigned to the actor.
	 * @throws RoleException if executing task does not have permission to
	 * execute this method.
	 */
	public Set<Role> getRoles(Actor actor) throws RoleException;

	/**
	 * Add experience to an actor
	 * 
	 * @param actor is the actor whose experience is being updated
	 * @param topic the topic being added to the actor's experience
	 * @throws RoleException if the calling task does not have permission for
	 * this operation
	 */
	public void updateExperience(Actor actor, Topic topic) throws RoleException;

	/**
	 * Add a role to an actor.
	 * 
	 * @param actor the actor who is being assigned a role.
	 * @param role a role being assigned to an actor.
	 * @throws RoleException if the calling task does not have the right to
	 * call this operation
	 */
	public void addRole(Actor actor, Role role);

	/**
	 * Removes a role from an actor.
	 * 
	 * @param actor the actor who is being removed a role.
	 * @param role a role being removed from an actor.
	 * @throws RoleException if the calling task does not have the right to
	 * call this operation
	 */
	public void removeRole(Actor actor, Role role);

	/**
	 * Add to an actor's set of interests
	 * 
	 * @param actor is the actor whose interests are being updated
	 * @param topic the topic being added to the actor's interests
	 * @throws RoleException if the calling task does not have permission for
	 * this operation
	 */
	public void updateInterests(Actor actor, Topic topic) throws RoleException;

	/**
	 * {@code PrmActorManager} is a default Java implementation of the actor
	 * management activity.
	 */
	static final class PrmActorManager implements ActorManager {

		private HashSet<Actor> actorSet;
		private ArtifactManager am;
		private Time creationDate;

		private static PrmActorManager instance;

		static {
			instance = new PrmActorManager();
		}

		private PrmActorManager() {
			this.actorSet = new HashSet<Actor>();
			this.creationDate = new Time();
			PrmActivityManager.getInstance().register(this);
			this.am = PrmArtifactManager.getInstance();
			am.registerArtifactType(PrmActor.class);
			addRole(Actor.ROOT_ACTOR, Role.ROOT_ROLE);
		}

		public static PrmActorManager getInstance() {
			return instance;
		}

		public Actor newActor(ContactInformation cinfo) {
			PrmSecurityManager.roleCheck("newActor", getClass(), ContactInformation.class);
			PrmActor actor = null;
			try {
				actor = (PrmActor) PRM.getArtifactManager().
				newArtifact(PrmActor.class, cinfo);
			} catch (UnregisteredArtifactType e) {
				e.printStackTrace();
			}
			this.actorSet.add(actor);
			return actor;
		}

		public Set<Actor> find(AttributePair<?>...attributes) {
			PrmSecurityManager.roleCheck("find", getClass(), AttributePair[].class);
			HashSet<Actor> results = new HashSet<Actor>(); 
			try {
				Directory d = am.lookup(attributes);
				Set<String> properties = d.getContextList();
				for ( String property : properties ) {
					ArtifactSet<? extends Artifact> arts = d.select(property);
					for ( Artifact artifact : arts.getContents() )
						if ( artifact instanceof Actor )
							results.add((Actor)artifact);
				}
				return results;
			} catch (Exception e) {
				e.printStackTrace();
			}
			return results;
		}

		public Set<Actor> find(Topic[] interests, Topic[] experience) {
			return this.find(new AttributePair<Topic[]>("getExperience", experience),
					new AttributePair<Topic[]>("getInterests", interests));
		}

		public Set<Actor> getActors() {
			PrmSecurityManager.roleCheck("getActors", getClass());
			HashSet<Actor> result = new HashSet<Actor>();
			result.addAll(actorSet);
			return result;
		}

		public Time getCreationDate() {
			return creationDate;
		}

		public String getDescription() {
			return "Default Actor Manager Implementation";
		}

		public VersionNumber getVersion() {
			return PRM.getPRMVersion();
		}

		public Set<Topic> getExperience(Actor actor) {
			PrmSecurityManager.roleCheck("getExperience", getClass(), Actor.class);
			return actor.getExperience();
		}

		public Set<Topic> getInterests(Actor actor) {
			PrmSecurityManager.roleCheck("getIntersts", getClass(), Actor.class);
			return actor.getInterests();
		}

		public Set<Role> getRoles(Actor actor) {
			PrmSecurityManager.roleCheck("getRoles", getClass(), Actor.class);
			return actor.getRoles();
		}

		public void updateExperience(Actor actor, Topic topic)
		throws RoleException {
			PrmSecurityManager.roleCheck("updateExperience", getClass(), new Class[] {Actor.class, Topic.class});
			((PrmActor)actor).addCompetence(topic);
		}

		public void updateInterests(Actor actor, Topic topic)
		throws RoleException {
			PrmSecurityManager.roleCheck("updateInterests", getClass(), new Class[] {Actor.class, Topic.class});
			((PrmActor)actor).addInterest(topic);
		}

		public void addRole(Actor actor, Role role) {
			PrmSecurityManager.roleCheck("addRole", getClass(), new Class[] {Actor.class, Role.class});
			PrmActor pa = (PrmActor)actor;
			pa.addRole(role);
		}

		public void removeRole(Actor actor, Role role) {
			PrmSecurityManager.roleCheck("removeRole", getClass(), new Class[] {Actor.class, Role.class});
			((PrmActor)actor).removeRole(role);
		}
	}
}
